package jmine.tec.web.wicket.pages.main;

import static jmine.tec.web.wicket.WebWicketMessages.PAGES_MAIN_LOGIN_DENIED;
import javascript.JSResources;

import javax.security.auth.Subject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import jmine.tec.web.wicket.security.SecureSession;
import jmine.tec.web.wicket.security.UserDetails;
import jmine.tec.web.wicket.upperCase.field.NoUpperCasePasswordTextField;
import jmine.tec.web.wicket.upperCase.field.NoUpperCaseTextFieldImpl;

import org.apache.wicket.Page;
import org.apache.wicket.PageParameters;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.ResourceReference;
import org.apache.wicket.Session;
import org.apache.wicket.feedback.FeedbackMessage;
import org.apache.wicket.markup.html.CSSPackageResource;
import org.apache.wicket.markup.html.JavascriptPackageResource;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.PasswordTextField;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.protocol.http.WebRequestCycle;
import org.apache.wicket.protocol.http.WebResponse;
import org.apache.wicket.request.target.basic.RedirectRequestTarget;
import org.apache.wicket.spring.injection.annot.SpringBean;

import bancosys.tec.persist.dao.BeanNotFoundException;
import bancosys.tec.security.SecurityException;
import bancosys.tec.security.SecurityService;
import bancosys.tec.security.SecurityURLMapping;
import bancosys.tec.security.impl.SecurityController;
import bancosys.tec.security.impl.UserDataLogger;
import bancosys.tec.security.impl.dao.SecurityClientDAO;
import bancosys.tec.security.impl.domain.SecurityClient;
import bancosys.tec.security.impl.web.WebSecurityContext;
import bancosys.tec.security.impl.web.WebSecurityManager;
import css.CSSResources;

/**
 * Pagina para login
 * 
 * @author piercio
 */
public class Login extends WebPage {

    private static final String ERROR_MESSAGE = "ERROR_MESSAGE";

    @SpringBean(name = "userDataLogger")
    private UserDataLogger userDataLogger;

    @SpringBean
    private WebSecurityManager securityManager;

    @SpringBean
    private SecurityService securityService;

    @SpringBean
    private SecurityController securityController;

    @SpringBean(name = "upperCaseLogin")
    private Boolean upperCaseLogin;

    /**
     * C'tor
     */
    public Login() {
        this.redirectIfNecessary();
        this.initForm();
    }

    /**
     * 
     */
    private void redirectIfNecessary() {
        if (SecurityURLMapping.getInstance().getLoginURL() != null) {
            // must redirect
            RequestCycle.get().setRedirect(true);
            RequestCycle.get().setRequestTarget(new RedirectRequestTarget(SecurityURLMapping.getInstance().getLoginURL()));
        }
    }

    /**
     * C'tor
     * 
     * @param parameters {@link PageParameters}
     */
    public Login(PageParameters parameters) {
        super(parameters);
        this.redirectIfNecessary();
        this.initForm();
    }

    /**
     * Inicializa o formulario
     */
    private void initForm() {
        this.addCSS("login.css");

        this.addJS("jquery-1.2.2.pack.js");
        this.addJS("magicButton.js");
        this.addJS("login.js");

        this.add(this.getUserForm());
        if (SecureSession.getSecureSession().getSubject() != null) {
            RequestCycle.get().setRedirect(true);
            this.setResponsePage(this.getResponsePage());
        }
        final Subject subject = this.securityService.getCurrentThreadSubject();
        if (subject != null) {
            // maybe first access and already logged in. Its safe to redirect.
            SecureSession.getSecureSession().setSubject(subject);
            RequestCycle.get().setRedirect(true);
            this.setResponsePage(this.getResponsePage());
        }

        HttpSession session = ((WebRequestCycle) RequestCycle.get()).getWebRequest().getHttpServletRequest().getSession();
        Object object = session.getAttribute(ERROR_MESSAGE);
        if (object != null) {
            session.removeAttribute(ERROR_MESSAGE);
            this.error(String.valueOf(object));
        }
    }

    /**
     * Cria e devolve o {@link Form} para usuario
     * 
     * @return {@link Form}
     */
    private Form<UserDetails> getUserForm() {
        UserDetails userDetails = new UserDetails();
        final Form<UserDetails> form = new Form<UserDetails>("securityForm") {
            @Override
            protected void onSubmit() {
                final UserDetails modelObject = this.getModelObject();
                SecureSession secureSession = SecureSession.getSecureSession();
                try {
                    HttpServletRequest request = this.getWebRequest().getHttpServletRequest();
                    HttpServletResponse response = ((WebResponse) this.getResponse()).getHttpServletResponse();
                    WebSecurityContext context = new WebSecurityContext(request, response);
                    Subject subject;
                    subject = Login.this.securityManager.login(modelObject.getUsername(), modelObject.getPassword(), context);
                    if (subject != null) {
                        Login.this.securityManager.storeSubject(context, subject);
                        secureSession.setSubject(subject);
                        if (secureSession.getPageMaps().isEmpty() || !secureSession.getPageMaps().get(0).continueToOriginalDestination()) {
                            RequestCycle.get().setRedirect(true);
                            this.setResponsePage(Login.this.getResponsePage());
                        }
                        Login.this.logUserLoginData(secureSession.getId(), modelObject.getUsername());
                    } else {
                        throw new SecurityException(PAGES_MAIN_LOGIN_DENIED.create());
                    }
                } catch (SecurityException e) {
                    ((WebRequestCycle) RequestCycle.get()).getWebRequest().getHttpServletRequest().getSession()
                            .setAttribute(ERROR_MESSAGE, e.getLocalizedMessage());
                    secureSession.getFeedbackMessages().add(this, e.getLocalizedMessage(), FeedbackMessage.ERROR);
                }
            }

        };
        form.setModel(new CompoundPropertyModel<UserDetails>(userDetails));

        String systemName = this.getSystemName();
        form.add(new Label("systemName", systemName));
        this.add(new Label("titleSystemName", systemName));

        TextField<String> usernameField;
        PasswordTextField passwordField;
        if (!this.upperCaseLogin) {
            usernameField = new NoUpperCaseTextFieldImpl("username");
            passwordField = new NoUpperCasePasswordTextField("password");
        } else {
            usernameField = new TextField<String>("username");
            passwordField = new PasswordTextField("password");
        }

        usernameField.setRequired(true);
        usernameField.setLabel(new Model<String>("Usuário"));

        passwordField.setRequired(true);
        passwordField.setLabel(new Model<String>("Senha"));

        form.add(usernameField);
        form.add(passwordField);

        this.add(new FeedbackPanel("feedback_login"));
        return form;
    }

    /**
     * @return Nome do sistema.
     */
    private String getSystemName() {
        SecurityClientDAO securityClientDAO = this.securityController.getDAOFactory().getDAOByClass(SecurityClientDAO.class);
        try {
            SecurityClient securityClient = securityClientDAO.findByPk(this.securityService.getSystemId());
            return securityClient.getName();
        } catch (BeanNotFoundException e) {
            return "Sistema";
        }
    }

    /**
     * Registra o acesso do usuário ao sistema.
     * 
     * @param sessionId o id da sessão.
     * @param username o nome do usuário.
     */
    private void logUserLoginData(String sessionId, String username) {
        if (this.userDataLogger != null) {
            this.userDataLogger.login(username, sessionId, String.valueOf(this.securityService.getSystemId()));
        }
    }

    /**
     * Hook para sobrescrever a page que será redirecionada após o login.
     * 
     * @return Class
     */
    protected Class<? extends Page> getResponsePage() {
        return Home.class;
    }

    /**
     * Adiciona um arquivo CSS a página.
     * 
     * @param cssFilename o nome do arquivo CSS.
     */
    protected void addCSS(String cssFilename) {
        this.add(CSSPackageResource.getHeaderContribution(new ResourceReference(CSSResources.class, cssFilename, Session.get().getLocale(),
                Session.get().getStyle())));
    }

    /**
     * Adiciona um arquivo JavaScript a página.
     * 
     * @param jsFilename o nome do arquivo JavaScript.
     */
    protected void addJS(String jsFilename) {
        this.add(JavascriptPackageResource.getHeaderContribution(new ResourceReference(JSResources.class, jsFilename, Session.get()
                .getLocale(), Session.get().getStyle())));
    }

}
